#include "operate.h"

extern struct pointMsg point;     //存放点表信息
extern MQTTClient client;         //客户端
extern struct std_node *addr;     //存放共享内存的地址
extern int *total_num_ptr;        //指向前四个字节的总数
extern struct std_node *data_ptr; //指向四个字节后的第一个结构体
extern sqlite3 *db;
extern char *errmsg; //接收表错误信息
//解析点表
struct pointMsg parsingPoint()
{
    char buf[POINT_MAX] = "";
    struct pointMsg point;
    cJSON *root = NULL;
    cJSON *temp = NULL;
    cJSON *val = NULL;

    point.port = -1;
    FILE *fd = NULL;
    if ((fd = fopen(CONFIG_PATH, "r")) == NULL)
    {
        perror("fopen error");
        return point;
    }

    //定位到开头
    rewind(fd);

    if (fread(buf, POINT_MAX, 1, fd) != 0)
    {
        printf("fread error\n");
        return point;
    }
    //printf("%s\n",buf);

    root = cJSON_Parse(buf);

    //mqtt服务器IP和端口号
    temp = cJSON_GetObjectItem(root, "mqtt_server");
    val = cJSON_GetObjectItem(temp, "addr");
    strcpy(point.addr, val->valuestring);

    val = cJSON_GetObjectItem(temp, "port");
    point.port = val->valueint;

    //数据上报属性
    temp = cJSON_GetObjectItem(root, "report");
    val = cJSON_GetObjectItem(temp, "type");
    point.report_type = val->valueint;

    val = cJSON_GetObjectItem(temp, "period");
    point.report_period = val->valueint;

    savePointToShame(buf);

    fclose(fd);
    return point;
}

//存入共享内存
void savePointToShame(char *buf)
{
    struct std_node *msg = data_ptr;

    //int number = 0; //记录载入了多少节点，防止超过最大节点
    int size = 0;
    cJSON *root = NULL;
    cJSON *temp = NULL;
    cJSON *data = NULL;
    cJSON *dataItem = NULL;
    cJSON *val = NULL;

    root = cJSON_Parse(buf);
    //获取STM32的数据节点
    temp = cJSON_GetObjectItem(root, "stm32");
    //获取data数组
    data = cJSON_GetObjectItem(temp, "data");
    size = cJSON_GetArraySize(data);
    for (int i = 0; i < size; i++)
    {
        dataItem = cJSON_GetArrayItem(data, i);
        (*total_num_ptr)++;
        if (*total_num_ptr >= NUM) //预留一位用于判断
        {
            break;
        }
        val = cJSON_GetObjectItem(dataItem, "key");
        msg->key = val->valueint;
        val = cJSON_GetObjectItem(dataItem, "type");
        msg->type = val->valueint;
        msg->dev_type = 1;
        msg++;
    }

    //获取modbus的数据节点
    temp = cJSON_GetObjectItem(root, "modbus");
    //获取data数组
    data = cJSON_GetObjectItem(temp, "data");
    size = cJSON_GetArraySize(data);
    for (int i = 0; i < size; i++)
    {
        dataItem = cJSON_GetArrayItem(data, i);
        (*total_num_ptr)++;
        if (*total_num_ptr >= NUM) //预留一位用于判断
        {
            break;
        }
        val = cJSON_GetObjectItem(dataItem, "key");
        msg->key = val->valueint;
        val = cJSON_GetObjectItem(dataItem, "type");
        msg->type = val->valueint;
        msg->dev_type = 2;
        msg++;
    }
    cJSON_Delete(root);
}
//连接mqtt
MQTTClient connectToMqtt(struct pointMsg point)
{
    char addr[32] = "";
    MQTTClient client;
    MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
    int rc;
    sprintf(addr, "%s:%d", point.addr, point.port);

    printf("%s\n", addr);

    MQTTClient_create(&client, addr, CLIENTID,
                      MQTTCLIENT_PERSISTENCE_NONE, NULL);
    conn_opts.keepAliveInterval = 20;
    conn_opts.cleansession = 1;

    MQTTClient_setCallbacks(client, NULL, connlost, msgarrvd, delivered);
    if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS)
    {
        printf("Failed to connect, return code %d\n", rc);
        return NULL;
    }
    printf("success to connect, return code %d\n", rc);
    //订阅指令
    MQTTClient_subscribe(client, TOPIC_DATA_DOWN, QOS);
    MQTTClient_subscribe(client, TOPIC_CTL_DOWN, QOS);
    MQTTClient_subscribe(client, TOPIC_MODE_DOWN, QOS);

    return client;
}
//连接断开
void connlost(void *context, char *cause)
{
    printf("\nConnection lost\n");
    printf("     cause: %s\n", cause);
}
void delivered(void *context, MQTTClient_deliveryToken dt)
{
    printf("Message with token value %d delivery confirmed\n", dt);
    // deliveredtoken = dt;
}

//接受消息处理，先根据主题确认，再按照type辨别
int msgarrvd(void *context, char *topicName, int topicLen, MQTTClient_message *message)
{
    //接受指令进行处理;
    char *payloadptr;
    char *send = NULL;
    cJSON *root = NULL;
    cJSON *temp = NULL;
    cJSON *data = NULL;
    struct msgbuf send_buf; //消息队列发送数组
    int key = 0;

    //3改变模式中的data
    int type_mode = -1;
    int period_mod = 0;

    int result = 1; //控制返回结果

    int num = *total_num_ptr;
    struct std_node *data_ptr_temp = data_ptr;

    LOGD("Message arrived");
    LOGD("topic: %s\n", topicName);

    payloadptr = message->payload;
    LOGD("%s", payloadptr);

    //解析指令,先判断能否进行解析
    root = cJSON_Parse(payloadptr);
    if (root == NULL)
    {
        LOGW("json解析失败，可能不是json字符串");
        MQTTClient_freeMessage(&message);
        MQTTClient_free(topicName);
        return -1;
    }

    //根据主题进入对应的判断
    if (strcmp(topicName, TOPIC_DATA_DOWN) == 0) //1,4 收到数据主题
    {
        LOGD("收到数据主题");
        //获取指令类型进行判断
        temp = cJSON_GetObjectItem(root, "type");
        if (temp == NULL)
        {
            LOGW("json中type获取失败-data");
        }
        else if (temp->valueint == 1)
        {
            LOGD("解析为数据刷新指令");
            dataUp();
        }
        else if (temp->valueint == 4)
        {
            LOGD("解析为历史记录获取指令");
            historyUp(); //TODO历史上传,只传了固定数据，需要做
        }
        else
        {
            LOGN("非数据处理指令");
        }
    }
    else if (strcmp(topicName, TOPIC_CTL_DOWN) == 0) //2 收到控制主题
    {
        LOGD("收到控制主题");
        //获取指令类型
        temp = cJSON_GetObjectItem(root, "type");
        if (temp == NULL)
        {
            LOGW("json中type获取失败");
        }
        else if (temp->valueint == 2)
        {
            LOGD("解析为控制指令");

            //拿到要转发数据的json
            temp = cJSON_GetObjectItem(root, "data"); //得到data，用于添加到消息队列
            if (temp == NULL)
            {
                LOGW("json中data object获取失败-control");
                //默认返回结果为1
            }

            //将数据转成字符串放进消息对了待发送数组里
            send = cJSON_Print(temp);
            if (send == NULL)
            {
                LOGN("消息队列要转发的数据获取失败");
                //默认返回结果为1
            }
            else
            {
                LOGD("将要添加到消息队列：%s", send);
                strcpy(send_buf.mdata, send);
                free(send);
            }

            //获取data中的key值，根据key值在共享内存中找到对应的设备类型
            data = cJSON_GetObjectItem(temp, "key");
            if (data == NULL)
            {
                LOGN("无法获取对应的key值");
            }
            key = data->valueint;
            for (int i = 0; i < num; i++)
            {
                if (key == data_ptr_temp[i].key)
                {
                    send_buf.mtype = data_ptr_temp[i].dev_type;
                    result = 0;
                    if (msg_queue_send("msg", &send_buf, sizeof(send_buf), 0) < 0)
                    {
                        LOGW("msg_queue_send error");
                        return -1;
                    }
                }
            }
            //转成回复的json格式
            cJSON_DeleteItemFromObject(root, "data");
            cJSON_AddNumberToObject(root, "result", result);
            if (!result)
            {
                cJSON_AddStringToObject(root, "msg", "控制成功");
            }
            else
            {
                cJSON_AddStringToObject(root, "msg", "控制失败");
            }

            send = cJSON_Print(root);
            if (send == NULL)
            {
                LOGW("回复控制要发送的json获取失败");
            }
            mqttSend(send, TOPIC_CTL_UP);
            free(send);
        }
        else
        {
            LOGN("非控制指令");
        }
    }
    else if (strcmp(topicName, TOPIC_MODE_DOWN) == 0) //3 收到配置模式主题
    {
        LOGD("收到模式主题");
        temp = cJSON_GetObjectItem(root, "type");
        if (temp == NULL)
        {
            LOGW("json中type获取失败-mode");
        }
        else if (temp->valueint == 3)
        {
            LOGD("解析为修改配置指令");
            //模式修改
            temp = cJSON_GetObjectItem(root, "data"); //得到data，用于添加到消息队列
            if (temp == NULL)
            {
                LOGW("json中data object获取失败-mode");
                //默认返回结果为1
            }
            data = cJSON_GetObjectItem(temp, "type");
            if (data == NULL)
            {
                LOGW("获取修改模式失败");
                //默认返回结果为1
            }
            type_mode = data->valueint;
            data = cJSON_GetObjectItem(temp, "period");
            if (data == NULL)
            {
                LOGW("获取延时周期失败");
                //默认返回结果为1
            }
            period_mod = data->valueint;

            //调用更改函数
            result = ChangeMode(type_mode, period_mod);

            //转成回复的json格式
            cJSON_DeleteItemFromObject(root, "data");
            cJSON_AddNumberToObject(root, "result", result);
            if (!result)
            {
                cJSON_AddStringToObject(root, "msg", "配置成功");
            }
            else
            {
                cJSON_AddStringToObject(root, "msg", "配置失败");
            }
            send = cJSON_Print(root);
            if (send == NULL)
            {
                LOGW("回复配置指令错误");
            }
            mqttSend(send, TOPIC_MODE_UP);
            free(send);
        }
        else
        {
            LOGN("非配置模式指令");
        }
    }
    cJSON_Delete(root);
    MQTTClient_freeMessage(&message);
    MQTTClient_free(topicName);
    return 1;
}

//根据上报方式控制上报
void sendCtr()
{
    int i = 0;

    int num = *total_num_ptr;
    struct std_node *temp = data_ptr;

    while (1)
    {
        switch (point.report_type)
        {
        case 0:
            sleep(1000);
            break;
        case 1: //变化上报
            while (1)
            {
                if (memcmp(&temp[i].new_val, &temp[i].old_val, sizeof(union val_t)))
                {
                    //发送函数
                    LOGD("比较刷新");
                    dataUp();
                }
                i++;
                if (i >= num)
                {
                    i = 0;
                    sleep(1);
                }
            }
            break;
        case 2: //周期上报
            LOGD("周期刷新");
            //发送函数
            dataUp();
            sleep(point.report_period);
            break;
        default:
            break;
        }
    }
}
//处理要上报的数据
void dataUp()
{
    int num = *total_num_ptr;
    struct std_node *temp = data_ptr;
    //printf("temp:%p\n",temp);
    char buf[32] = "";

    //序列化
    cJSON *root = cJSON_CreateObject(); //创建主对象
    cJSON *array = cJSON_CreateArray(); //创建数组
    cJSON_AddNumberToObject(root, "type", 1);

    //result还需要判断获取是否成功
    cJSON_AddNumberToObject(root, "result", 0);

    cJSON_AddItemToObject(root, "data", array);

    for (int i = 0; i < num; i++)
    {
        cJSON *dx = cJSON_CreateObject(); //创建对象
        cJSON_AddItemToArray(array, dx);
        cJSON_AddNumberToObject(dx, "key", temp[i].key);
        LOGD("数据转换\n");
        switch (temp[i].type)
        {
        case 1:
            sprintf(buf, "%d", temp[i].new_val.b_val);
            LOGD("bool:%s\n", buf);
            cJSON_AddStringToObject(dx, "val", buf);
            temp[i].old_val.b_val = temp[i].new_val.b_val;
            break;
        case 2:
            sprintf(buf, "%d", temp[i].new_val.i_val);
            LOGD("int:%s\n", buf);
            cJSON_AddStringToObject(dx, "val", buf);
            temp[i].old_val.i_val = temp[i].new_val.i_val;
            break;
        case 3:
            sprintf(buf, "%.2f", temp[i].new_val.f_val);
            LOGD("float:%s\n", buf);
            cJSON_AddStringToObject(dx, "val", buf);
            temp[i].old_val.f_val = temp[i].new_val.f_val;
            break;
        default:
            break;
        }
    }
    LOGD("数据准备发送");
    //准备发送
    char *str;
    str = cJSON_Print(root);
    //printf("%s\n",str);
    //发送
    mqttSend(str, TOPIC_DATA_UP);
    free(str);
    cJSON_Delete(root);
}
//发送到对应的主题
void mqttSend(char *str, char *topic)
{
    MQTTClient_message pubmsg = MQTTClient_message_initializer;
    MQTTClient_deliveryToken token;

    pubmsg.qos = QOS;
    pubmsg.retained = 0;
    pubmsg.payload = str;
    pubmsg.payloadlen = (int)strlen(str);
    MQTTClient_publishMessage(client, topic, &pubmsg, &token);
    MQTTClient_waitForCompletion(client, token, TIMEOUT);
    LOGD("Message with delivery token %d delivered\n", token);
}
//改变配置点表
int ChangeMode(int type, int period)
{
    char buf[POINT_MAX] = "";
    char *str = NULL;
    cJSON *root = NULL;
    cJSON *temp = NULL;

    point.port = -1;
    FILE *fd = NULL;
    if ((fd = fopen(CONFIG_PATH, "r+")) == NULL)
    {
        perror("fopen error");
        return 1;
    }

    if (fread(buf, POINT_MAX, 1, fd) != 0)
    {
        printf("fread error\n");
        return 1;
    }

    //定位到开头
    rewind(fd);

    root = cJSON_Parse(buf);
    temp = cJSON_GetObjectItem(root, "report");
    cJSON_DeleteItemFromObject(temp, "type");
    cJSON_DeleteItemFromObject(temp, "period");
    cJSON_AddNumberToObject(temp, "type", type);
    cJSON_AddNumberToObject(temp, "period", period);

    str = cJSON_Print(root);
    printf("%s\n", str);

    if (fwrite(str, strlen(str), 1, fd) < 0)
    {
        printf("fwrite error\n");
        return 1;
    }

    free(str);
    cJSON_Delete(root);
    fclose(fd);
    return 0;
}
//历史记录上传
void historyUp()
{
    char *str = NULL;
    //序列化
    cJSON *root = cJSON_CreateObject(); //创建主对象
    cJSON *array = cJSON_CreateArray(); //创建数组
    cJSON_AddNumberToObject(root, "type", 4);

    cJSON_AddNumberToObject(root, "key", 301);// TODO

    //result还需要判断获取是否成功
    cJSON_AddNumberToObject(root, "result", 0);
    cJSON_AddItemToObject(root, "data", array);

    //TODO数据库是否打开
    char sql[128] = {0};
    sprintf(sql, "select * from data_table where key=101 and time>2022112818100 and time <2022112818200;");
    if (sqlite3_exec(db, sql, historyCallback, array, &errmsg) != 0)
    {
        fprintf(stderr, "insert err:%s\n", errmsg);
    }
    str = cJSON_Print(root);
    mqttSend(str, TOPIC_DATA_UP);
    free(str);
    cJSON_Delete(root);
}

//数据库查询的回调函数
int historyCallback(void *para, int f_num, char **f_value, char **f_name)
{
    cJSON *root =(cJSON *)para;
    cJSON *temp = cJSON_CreateObject(); //创建主对象
    cJSON_AddItemToObject(root, "NULL", temp);

    cJSON_AddStringToObject(temp,"time",f_value[0]);
    cJSON_AddStringToObject(temp,"val",f_value[2]);
    LOGD("time:%s\n", f_value[0]);
    LOGD("data:%s\n", f_value[2]);
    return 0;
}